home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / human interface toolbox / progressbars / threadedprogress.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-06-23  |  7.6 KB  |  300 lines

  1. /*
  2.     File:        ThreadedProgress.c
  3.  
  4.     Contains:    Progress bar implementation using the Thread Manager
  5.  
  6.     Written by: Chris White    
  7.  
  8.     Copyright:    Copyright © 1996-1999 by Apple Computer, Inc., All Rights Reserved.
  9.  
  10.                 You may incorporate this Apple sample source code into your program(s) without
  11.                 restriction. This Apple sample source code has been provided "AS IS" and the
  12.                 responsibility for its operation is yours. You are not permitted to redistribute
  13.                 this Apple sample source code as "Apple sample source code" after having made
  14.                 changes. If you're going to re-distribute the source, we require that you make
  15.                 it clear in the source that the code was descended from Apple sample source
  16.                 code, but that you've made changes.
  17.  
  18.     Change History (most recent first):
  19.                 8/10/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
  20.                 
  21.  
  22. */
  23.  
  24.  
  25. #pragma segment Core
  26.  
  27.  
  28. // System Includes
  29.  
  30.  
  31. #include <Threads.h>
  32.  
  33.  
  34.  
  35. // Application includes
  36.  
  37. #ifndef __BAREBONES__
  38.     #include "BareBones.h"
  39. #endif
  40.  
  41. #ifndef __PROTOTYPES__
  42.     #include "Prototypes.h"
  43. #endif
  44.  
  45.  
  46. // static prototypes
  47.  
  48. static pascal void* ProgressDialogThread ( tThreadedOperationPtr theInfo );
  49. static pascal void    ThreadTermination ( ThreadID threadTerminated, void* terminationProcParam );
  50.  
  51.  
  52.  
  53.  
  54.  
  55. OSErr ThreadedProgressOperation ( tThreadedOperation theOperation, void* refCon,
  56.                     StringPtr theText, SInt32* operationErr, Boolean bBarberPole )
  57. {
  58.     OSErr                    theErr = noErr;
  59.     ThreadID                operationsThreadID = 0;
  60.     ThreadID                progressDlgThreadID = 0;
  61.     tThreadedOperationPtr    theInfo = nil;
  62.     
  63.     
  64.     
  65.     theInfo = (tThreadedOperationPtr) NewPtrClear ( sizeof ( tThreadedOperationRec ) );
  66.     theErr = MemError ( );
  67.     if ( theErr )    goto CleanupAndbail;
  68.     
  69.     BlockMoveData ( theText, theInfo->theText, theText[0] + 1 );
  70.     theInfo->bBarberPole = bBarberPole;
  71.     theInfo->refCon = refCon;
  72.     
  73.     
  74.     // Create the Progress Dialog Thread
  75.     theErr = NewThread ( kCooperativeThread, (ThreadEntryProcPtr) ProgressDialogThread,
  76.                             (void*) theInfo, kDefaultStackSpace, kNoCreationOptions,
  77.                             (void*) operationErr, &progressDlgThreadID );
  78.     if ( theErr )    goto CleanupAndbail;
  79.     
  80.     // Install termination routine to decrement the usage count
  81.     // and maybe dispose of the tThreadedOperationRec.
  82.     theInfo->usageCount++;
  83.     theErr = SetThreadTerminator ( progressDlgThreadID, ThreadTermination, (void*) theInfo );
  84.  
  85.     // Create the Operation Thread 
  86.     theErr = NewThread ( kCooperativeThread, (pascal void* (*) (void*)) theOperation, (void*) theInfo,
  87.                             kDefaultStackSpace, kNoCreationOptions, (void*) operationErr,
  88.                             &operationsThreadID );
  89.     if ( theErr )    goto CleanupAndbail;
  90.     
  91.     theInfo->usageCount++;
  92.     theErr = SetThreadTerminator ( operationsThreadID, ThreadTermination, (void*) theInfo );
  93.     
  94.     // Get the dialog drawn _before_ the actual operation is started
  95.     theErr = YieldToThread ( progressDlgThreadID );
  96.     if ( theErr )    goto CleanupAndbail;
  97.  
  98.     return noErr;
  99.     
  100. CleanupAndbail:
  101.     
  102.     if ( theInfo )
  103.         DisposePtr ( (Ptr) theInfo );
  104.         
  105.     // Dispose of the threads, and pass the error codes back
  106.     if ( operationsThreadID )
  107.         DisposeThread ( operationsThreadID, (void*) theErr, false );
  108.     
  109.     if ( operationsThreadID )
  110.         DisposeThread ( progressDlgThreadID, (void*) theErr, false );
  111.     
  112.     return theErr;
  113. }
  114.  
  115.  
  116.  
  117. static pascal void* ProgressDialogThread ( tThreadedOperationPtr theInfo )
  118. {
  119.     SInt16        theType;
  120.     GrafPtr        savePort;
  121.     DialogRef    theDialog = nil;
  122.     Handle        theHan;
  123.     Rect        theRect;
  124.     
  125.     
  126.     theDialog = GetNewDialog ( kProgressDialogID, nil, (WindowPtr) -1 );
  127.     
  128.     SetWRefCon ( theDialog, (long) theInfo );
  129.     
  130.     GetDialogItem ( theDialog, kStaticTextItemID, &theType, &theHan, &theRect );
  131.     SetDialogItemText ( theHan, theInfo->theText );
  132.     
  133.     GetDialogItem ( theDialog, kUserItemID, &theType, &theHan, &theRect );
  134.     if ( !theInfo->bBarberPole )
  135.     {
  136.         SetDialogItem ( theDialog, kUserItemID, theType, (Handle) gOutlineUserItemUPP, &theRect );
  137.         CallUserItemProc ( gOutlineUserItemUPP, theDialog, kUserItemID );
  138.     }
  139.     
  140.     ShowWindow ( theDialog );
  141.     DrawDialog ( theDialog );
  142.     
  143.     
  144.     while ( theInfo->bCancelled == false )
  145.     {
  146.         YieldToAnyThread ( );
  147.         
  148.         // Although the user interface doesn't allow you to execute
  149.         // more than one threaded progress bar, we'll be careful to
  150.         // support it here. It's just a case of making sure the port
  151.         // is setup and restored across calls to YieldToAnyThread.
  152.         
  153.         GetPort ( &savePort );
  154.         SetPort ( theDialog );
  155.         
  156.         if ( theInfo->bBarberPole )
  157.         {
  158.             PicHandle        thePic;
  159.             static SInt16    theID = 1000;        // Gotcha: Non-reentrant, see documentation
  160.             
  161.             
  162.             // Gotcha: Some Resource Manager calls can only be made
  163.             // from the main thread on a Mac Plus. See documentation
  164.             thePic = GetPicture ( theID++ );
  165.             DrawPicture ( thePic, &theRect );
  166.             if ( theID > 1003 )
  167.                 theID = 1000;
  168.             
  169.             if ( theInfo->doneAmount == kBarberPoleFinished )
  170.                 break;
  171.         }
  172.         else
  173.         {
  174.             if ( theInfo->doneAmount != theInfo->drawnAmount )
  175.             {
  176.                 int        theLength;
  177.                 float    floatDone, floatMax, thePercent;
  178.                 
  179.                 // Temporarily adjust the user item rect to draw the bar
  180.                 GetDialogItem ( theDialog, kUserItemID, &theType, &theHan, &theRect );
  181.                 theLength = theRect.right - theRect.left;
  182.                 floatDone = theInfo->doneAmount;
  183.                 floatMax = theInfo->maxAmount;
  184.                 thePercent = (floatDone / floatMax) * 100;
  185.                 theRect.right = theRect.left + ((thePercent / 100) * theLength);
  186.                 
  187.                 theRect.top--; theRect.bottom++;
  188.                 FillRect ( &theRect, &qd.black );
  189.             }
  190.             
  191.             if ( theInfo->doneAmount == theInfo->maxAmount )
  192.                 break;
  193.         }
  194.         
  195.         SetPort ( savePort );
  196.     }
  197.     
  198.     // A problem occurs if the thread is terminated before the dialog is
  199.     // disposed. Fortunatly, with the current implementation, it can't.
  200.     // However, if this can occur with your implementation, you'll want
  201.     // to dispose of the dialog and (optionally?) restore the port in the
  202.     // termination routine.
  203.     DisposeDialog ( theDialog );
  204.     
  205.     return noErr;
  206. }
  207.  
  208.  
  209.  
  210. //
  211. // We don't want to dispose of the record when another thread could still
  212. // be accessing it. Although they'll both finish about the same time, we
  213. // don't want to rely on the implementation of the thread scheduler. This
  214. // approach ensures both threads have finished with the record.
  215. //
  216. static pascal void ThreadTermination ( ThreadID threadTerminated, void* terminationProcParam )
  217. {
  218.     #pragma unused(threadTerminated)
  219.     OSErr    theErr;
  220.     
  221.     
  222.     #if DEBUGGING
  223.     if ( terminationProcParam == nil )
  224.         DebugStr ( "\p ThreadTermination: terminationProcParam is nil" );
  225.     #endif
  226.     
  227.     
  228.     ((tThreadedOperationPtr) terminationProcParam)->usageCount--;
  229.     if ( ((tThreadedOperationPtr) terminationProcParam)->usageCount == 0 )
  230.     {
  231.         DisposePtr ( (Ptr) terminationProcParam );
  232.         
  233.         #if DEBUGGING
  234.         theErr = MemError ( );
  235.         if ( theErr )    DebugStrNum ( "\p ThreadTermination: DisposePtr", theErr );
  236.         #endif
  237.     }
  238.     
  239.     return;
  240. }
  241.  
  242.  
  243.  
  244. //
  245. // This routine is one of the operations carried out
  246. // which the progress bar is representing.
  247. //
  248. pascal SInt32 ThreadedStandardDemoOperation ( tThreadedOperationPtr theInfo )
  249. {
  250.     OSErr        theErr = noErr;
  251.     int            i;
  252.     const int    max = 100;
  253.     
  254.     
  255.     for ( i = 1; i <= max && theInfo->bCancelled == false; i++ )
  256.     {
  257.         UInt32    theDelay = 10L;
  258.         Delay ( theDelay, &theDelay );
  259.         
  260.         theInfo->doneAmount = i;
  261.         theInfo->maxAmount = max;
  262.         
  263.         YieldToAnyThread ( );
  264.     }
  265.     
  266.     return (SInt32) theErr;
  267. }
  268.  
  269.  
  270.  
  271. //
  272. // This routine is one of the operations carried out
  273. // which the progress bar is representing.
  274. //
  275. pascal SInt32 ThreadedBarberPoleDemoOperation ( tThreadedOperationPtr theInfo )
  276. {
  277.     // A5 is not garanteed to be valid
  278.     
  279.     OSErr        theErr = noErr;
  280.     int            i;
  281.     const int    max = 100;
  282.     
  283.     
  284.     for ( i = 1; i <= max && theInfo->bCancelled == false; i++ )
  285.     {
  286.         UInt32    theDelay = 10L;
  287.         Delay ( theDelay, &theDelay );
  288.         
  289.         YieldToAnyThread ( );
  290.     }
  291.     
  292.     theInfo->doneAmount = kBarberPoleFinished;
  293.     
  294.     return (SInt32) theErr;
  295. }
  296.  
  297.  
  298.  
  299.  
  300.